package handler

import (
	"encoding/binary"
	"fmt"
	"github.com/gorilla/websocket"
	"sen-golang-study/go_game_study/hero_story_go_server/common/log"
	"sen-golang-study/go_game_study/hero_story_go_server/common/main_thread"
	"sen-golang-study/go_game_study/hero_story_go_server/common/server_msg"
	"time"
)

type ClientMsgHandler struct {
	ClientId            uint32
	ClientConn          *websocket.Conn
	MsgChan             chan *server_msg.InternalServerMsg
	ctxMapLastClearTime int64
}

func (h *ClientMsgHandler) InitAndStart() {
	h.MsgChan = make(chan *server_msg.InternalServerMsg, 64)

	go h.LoopSendMsg()
	h.LoopReadMsg()
}

func (h *ClientMsgHandler) LoopSendMsg() {
	for {
		msgObj := <-h.MsgChan
		if nil == msgObj {
			continue
		}

		byteArray := msgObj.ToByteArray()

		err := h.ClientConn.WriteMessage(websocket.BinaryMessage, byteArray)
		if err != nil {
			log.Error("%+v", err)
		}
	}
}

func (h *ClientMsgHandler) LoopReadMsg() {
	if h == nil {
		return
	}

	for {
		_, msgData, err := h.ClientConn.ReadMessage()

		if nil != err {
			log.Error("%v+", err)
			break
		}

		innerMsg := &server_msg.InternalServerMsg{}
		innerMsg.FromByteArray(msgData)

		log.Info("接收到消息：%v", innerMsg)

		ctxGroup := GetCmdContextGroup()
		ctxId := fmt.Sprintf("%d_%d", innerMsg.GatewayId, innerMsg.SessionId)

		ctx, _ := ctxGroup.innerMap.Load(ctxId)

		if innerMsg.Disconnect == 1 {
			HandleOnUserQuit(ctx)
			continue
		}

		realMsgData := innerMsg.MsgData

		msgCode := binary.BigEndian.Uint16(realMsgData[2:4])
		newMsgX, err := server_msg.Decode(realMsgData[4:], int16(msgCode))

		if nil != err {
			log.Error(
				"消息解码错误, msgCode = %d, error = %+v",
				msgCode, err,
			)
			continue
		}

		log.Info(
			"收到客户端消息,sessionId=%d,userId=%d, msgCode = %d, msgName = %s",
			innerMsg.SessionId,
			innerMsg.UserId,
			msgCode,
			newMsgX.Descriptor().Name(),
		)

		cmdHandler := CreateCmdHandler(msgCode)

		if nil == cmdHandler {
			log.Error(
				"未找到指令处理器, msgCode = %d",
				msgCode,
			)
			continue
		}

		if ctx == nil {
			newCtx := &CmdContextInnerWebsocket{
				Id:              ctxId,
				GatewayId:       innerMsg.GatewayId,
				RemoteSessionId: innerMsg.SessionId,
				UserId:          innerMsg.UserId,
				ClientIpAddr:    "",
				MsgChan:         h.MsgChan,
			}

			log.Info("新建命令上下文，上下文ID=%s", ctxId)
			ctx, _ = ctxGroup.innerMap.LoadOrStore(ctxId, newCtx)
		}

		innerCtx := ctx.(*CmdContextInnerWebsocket)
		innerCtx.LastActiveTime = time.Now().UnixMilli()
		main_thread.PushTask(func() {
			cmdHandler(ctx, newMsgX)
		})

		GetCmdContextGroup().Register(ctxId, ctx)
		h.clearCtxMap()
	}

}

func (h *ClientMsgHandler) clearCtxMap() {
	nowTime := time.Now().UnixMilli()

	if nowTime-h.ctxMapLastClearTime < 120000 {
		// 若上次清除时间间隔未超过2分钟，则不执行
		return
	}

	delectCtxIds := make([]interface{}, 64)

	GetCmdContextGroup().innerMap.data.Range(func(key, val interface{}) bool {
		if key == nil || val == nil {
			return true
		}

		currCtx := val.(*CmdContextInnerWebsocket)

		if nowTime-currCtx.LastActiveTime < 120000 {
			// 若currCtx上次活动时间在2分钟内，则保留不做处理
			return true
		}

		delectCtxIds = append(delectCtxIds, key)
		return true
	})

	for _, key := range delectCtxIds {
		if key == nil {
			continue
		}

		log.Info("删除Ctx，CtxId=%v", key)
		GetCmdContextGroup().UnRegister(key.(string))
	}

	h.ctxMapLastClearTime = nowTime
}
